I18N For React

Αν δεν βρίσκεστε σε μια αγγλόφωνη αγορά και δεν αναμένετε ποτέ να έχετε πελάτες που δεν μιλούν αγγλικά, έχετε τουλάχιστον μια φορά σκοντάψει στο πρόβλημα της διεθνοποίησης σελίδων, τουλάχιστον στην πιο συνηθισμένη περίπτωση, μετάφραση σελίδων.

Ως προγραμματιστής πανεπιστημιακού λογισμικού στην Πορτογαλία, το i18n είναι βασική απαίτηση των εφαρμογών. Όλες οι εφαρμογές προφανώς πρέπει να είναι στα πορτογαλικά, καθώς ορισμένοι μεγαλύτεροι χρήστες δεν ξέρουν πώς να μιλούν αγγλικά. Λόγω της μεγάλης δημοτικότητας των προγραμμάτων ανταλλαγής φοιτητών, η εφαρμογή πρέπει επίσης να είναι στα αγγλικά, την «καθολική» γλώσσα.

Κάνοντας την πρώτη μου πλήρη εφαρμογή React από την πλευρά του πελάτη, σκέφτηκα ότι θα ήταν ικανοποιητικό για εμένα να προσθέσω υποστήριξη για το i18n.

Η πρώτη μου προσέγγιση

Στην πρώτη μου προσέγγιση προσπάθησα να χρησιμοποιήσω μια βιβλιοθήκη που έφτιαξε ένας φίλος. Αυτή η βιβλιοθήκη λειτουργεί με βάση τα χαρακτηριστικά data-* που προστίθενται στα στοιχεία HTML. Η τιμή που δίνεται σε αυτά τα χαρακτηριστικά καθορίζει τα κλειδιά μηνύματος που θα εισαχθούν στα στοιχεία. Μέσω μιας απλής μεθόδου κλήσης, ολόκληρη η σελίδα μπορεί να μεταφραστεί ξανά χωρίς ανανέωση πλήρους σελίδας. Όλα φαίνονταν τέλεια και μου άρεσε πολύ η απλότητά του, αλλά δεν ταίριαζε στο React.

Δεδομένου ότι το React βασίζεται σε στοιχεία και τυχαίνει να χρησιμοποιώ κυρίως στοιχεία τρίτων, όπως το react-mdl και το react-autocomplete, δεν έχω πρόσβαση στα περισσότερα από τα εσωτερικά στοιχεία html. Αυτό σημαίνει ότι δεν μπορώ (εύκολα) να προσθέσω τα χαρακτηριστικά δεδομένων σε αυτά τα στοιχεία και δεν μπορώ να χρησιμοποιήσω αυτό το εργαλείο για να πραγματοποιήσω την τοπική προσαρμογή μου.

Τι υπάρχει στην κοινότητα React

Δεδομένου ότι η πρώτη μου προσέγγιση δεν επρόκειτο να λειτουργήσει, πήγα στο Google μερικές λύσεις και κατέληξα στο Yahoo να χρησιμοποιήσω αυτήν τη λύση: https://github.com/yahoo/react-intl

Το πρώτο μου μεγάλο πρόβλημα ήταν να βρω πώς να μεταφράσω δυναμικά την εφαρμογή. Φαίνεται ότι το πλαίσιο δεν έχει πολύ γνώμη σχετικά με τις δυναμικές ενημερώσεις, επομένως κάθε άτομο πρέπει να δημιουργήσει τη δική του λύση, η οποία κατά την άποψή μου θα πρέπει να δίνεται εκτός συσκευασίας ως μέρος του εργαλείου.

Για την εκτέλεση της μετάφρασης, αυτό το πλαίσιο προσφέρει στοιχεία που λαμβάνουν ένα κλειδί και εμφανίζουν τη μεταφρασμένη τιμή. Τότε συνειδητοποίησα ότι επρόκειτο να σκοντάψω στο ίδιο πρόβλημα όπως στην πρώτη μου προσέγγιση. Τα περισσότερα από τα out of the box συστατικά που χρησιμοποιώ έχουν μηνύματα που ορίζονται ως ιδιότητες. Για παράδειγμα, το TextField στο react-mdl λαμβάνει την ετικέτα ως ιδιότητα, όχι ως εσωτερικό στοιχείο. Αυτό σημαίνει ότι για άλλη μια φορά δεν θα μπορούσα να μεταφράσω ορισμένα μέρη της εφαρμογής.

Το React έχει να κάνει με τα συστατικά, ωστόσο, αυτό με έκανε να συνειδητοποιήσω ότι τα συστατικά δύσκολα θα είναι μια λύση για το i18n.

Η παλιά λύση και το πρόβλημα που λείπει

Δεδομένου ότι θα χρειαστεί να μεταφράσω τα κλειδιά πριν τα περάσω ως ιδιότητες σε άλλα στοιχεία, αποφάσισα να χρησιμοποιήσω την παλιά προσέγγιση: καλέστε μια συνάρτηση i18n που κάνει αυτή τη δουλειά. Υπάρχουν πολλές λύσεις εκεί έξω, για παράδειγμα i18next και Polyglot. Αποφάσισα να πάω με την Polyglot.

Αλλά αυτό με κράτησε ακόμα με ένα πρόβλημα: πώς να ενημερώσω δυναμικά τις προβολές μετά από ένα αίτημα αλλαγής τοπικών ρυθμίσεων;

Η πιο εύκολη λύση θα ήταν να αλλάξω απλώς την ιδιότητα κλειδιού του στοιχείου root μου, οδηγώντας σε μια ολόκληρη εκ νέου απόδοση της σελίδας. Ωστόσο, αυτή η λύση είναι αναποτελεσματική, όταν αλλάξουμε το κλειδί, το react θα θεωρήσει το στοιχείο ως διαφορετικό στοιχείο και το DOM θα πρέπει να αποδοθεί εκ νέου, παρακάμπτοντας τις φοβερές βελτιστοποιήσεις που γίνονται από το React on change component. Η κλήση του forceUpdate στο ριζικό στοιχείο πιθανότατα θα παρακάμψει τα εσωτερικά στοιχεία, καθώς οι καταστάσεις τους δεν άλλαξαν.

Μείγματα για τη διάσωση

Η λύση μου ήταν να δημιουργήσω ένα mixin το οποίο θα μπορούσα να εφαρμόσω στα εξαρτήματά μου που γνωρίζουν i18n. Προσθέτοντας απλώς αυτό το mixin σε όλατα στοιχεία που εκτελούν i18n, θα κληθεί μια ανανέωση δύναμης πάνω από τα στοιχεία.

Κάτω από την κουκούλα αυτό που κάνει αυτό το mixin είναι να ακούει ένα κατάστημα redux. Για να ενεργοποιήσουμε μια μετάφραση σελίδας, απλώς καλούμε τη μέθοδο αποστολής σε αυτό το κατάστημα με τη νέα τοπική ρύθμιση.

Αυτή η λύση, σε συνεργασία με τις κλήσεις της μεθόδου i18n μέσω Polyglot, κατέληξε να λύσει όλα τα προβλήματα που είχα με αποτελεσματικό τρόπο. Μπορείτε να ελέγξετε τον κωδικό και το API στο GitHub.

Επιπλέον δυνατότητες

Εάν δημιουργείτε μια τεράστια εφαρμογή ή/και διανέμετε την εφαρμογή σας σε πολλές γλώσσες, ίσως θέλετε να αποφύγετε την επιβολή όλου του περιεχομένου της γλώσσας σας σε όλους τους χρήστες σας, αποφεύγοντας να καταναλώνετε πολύτιμο δίκτυο.

Για να καλύψω αυτήν την ανάγκη πρόσθεσα τοπικές ρυθμίσεις ανάκτησης από το κουτί. Όταν ένας χρήστης αλλάζει τη γλώσσα, θα υποβληθεί αίτημα GET για την ανάκτηση ενός αρχείου με το όνομα «‹path›/‹locale›.json». Αυτή η διαδρομή πρέπει να ρυθμιστεί από τον χρήστη της βιβλιοθήκης.

Πρόσθεσα επίσης μια μέθοδο από την πλευρά του διακομιστή για την ανάγνωση του JSON. Θα πρέπει να χρησιμοποιήσετε αυτήν τη μέθοδο για να εισάγετε τα τρέχοντα δεδομένα τοπικής ρύθμισης στη σελίδα. Με αυτόν τον τρόπο, η πρώτη σας σελίδα θα έχει ήδη τα μεταφρασμένα μηνύματα που απαιτεί, αποφεύγοντας ένα αίτημα GET έως ότου ο χρήστης αλλάξει ρητά τις τοπικές ρυθμίσεις.

Πώς το κάνουν από εκεί που είστε;

Αυτή ήταν η λύση μου για το πρόβλημα i18n, αλλά είμαι βέβαιος ότι άλλα άτομα έχουν ήδη διορθώσει αυτό το πρόβλημα στο παρελθόν. Εχετε? Αφήστε ένα σχόλιο παρακάτω με τη λύση σας και αν νομίζετε ότι είναι καλή, προτείνετέ την και μοιραστείτε την!