1 <?php
2 /**
3 * Contains i18n class for Alphred, providing basic translation functionality
4 *
5 * PHP version 5
6 *
7 * @package Alphred
8 * @copyright Shawn Patrick Rice 2014
9 * @license http://opensource.org/licenses/MIT MIT
10 * @version 1.0.0
11 * @author Shawn Patrick Rice <rice@shawnrice.org>
12 * @link http://www.github.com/shawnrice/alphred
13 * @link http://shawnrice.github.io/alphred
14 * @since File available since Release 1.0.0
15 *
16 */
17
18 namespace Alphred;
19
20 /**
21 * Translation library for Alphred
22 *
23 * Right now, this is sort of "proof-of-concept" and works well enough for static strings, but
24 * it does need to be improved _vastly_.
25 *
26 * Right now, the best part about this library is that it will not break anything. So, if you
27 * try to implement it and fuck it up, it'll just do nothing rather than break things.
28 *
29 * For internationalization:
30 *
31 * gettext() isn't compiled into OS X's PHP installation, so I've created my own hacky version.
32 *
33 * (1) Create a directory called "i18n" in your workflow folder.
34 * (2) For each language you want to use, create a file called ln.json (lowercase) where
35 * "ln" is the two-letter language code: i.e. en = English, de = German, etc...
36 * (3) In that json, make the original string as the key and the value as the translated test.
37 * i.e., a file called i18n/fr.json would contain:
38 * {
39 * "Hello": "Bonjour",
40 * "Do you speak French?": "Parlez-vous Français?",
41 * "I am a grapefruit": "Je suis un pamplemousse",
42 * }
43 *
44 * (4) Make sure you escape the string if necessary.
45 *
46 * Tip: if you'd rather have the json created for you, then just do something like...
47 * file_put_contents( 'i18n/fr.json', json_encode( [
48 * 'string' => 'translated',
49 * 'string2' => 'second translation'
50 * ], JSON_PRETTY_PRINT );
51 *
52 * Then just execute that PHP, and the json file will be created for you.
53 *
54 **/
55 class i18n {
56
57 /**
58 * Constructs the i18n object to use for translation, setting language
59 *
60 * Right now $engine is unused, but it's there to open for expansion for
61 * other translation methods that can be popped in or out. `Json` is just
62 * what is described above.
63 *
64 * @param string $engine translation utility; json is the only option
65 */
66 public function __construct( $engine = 'json' ) {
67 if ( ! file_exists( 'i18n' ) || ! is_dir( 'i18n' ) ) { return false; }
68 // This is internal, for testing. If the "ALPHRED_TESTING" flag is set,
69 // then we'll pretend that our language is French rather than the system
70 // default (for me: English). Consider this testing code.
71 if ( ! defined( 'ALPHRED_TESTING' ) || ( true !== ALPHRED_TESTING ) ) {
72 $locale = exec( 'defaults read .GlobalPreferences AppleLanguages | tr -d [:space:] | cut -c2-3' );
73 } else {
74 $locale = 'fr';
75 }
76 if ( file_exists( "i18n/{$locale}.json" ) ) {
77 $this->locale = $locale;
78 } else {
79 return false;
80 }
81
82 // Try to load the translation "dictionary"
83 try {
84 $this->dictionary = json_decode( file_get_contents( "i18n/{$locale}.json" ), true );
85 } catch ( Exception $e ) {
86 // Well, the translation dictionary is not good JSON. So let's just log an error to the console
87 // and pretend this never happened.
88 file_put_contents( 'php://stderr', "Error: locale '{$locale}.json' file is not valid json." );
89 return false;
90 }
91
92 return $this;
93 }
94
95 /**
96 * Translates a string from a dictionary
97 *
98 * @todo use "engines" instead of a single method
99 *
100 * @param string $string a string to translate
101 * @return string a, possibly, translated string
102 */
103 public function translate( $string ) {
104 if ( ! isset( $this->locale ) ) { return $string; }
105 if ( isset( $this->dictionary[ $string ] ) ) {
106 return $this->dictionary[ $string ];
107 }
108 return $string;
109 }
110 }
111
112 // In the future, it would be pretty badass if I could give the option to do a background translation
113 // using Google Translate or something akin to that.
114
115 // For now, we'll leave this as is. In future releases, we'll create more engines that should work better
116 // than this.